package flexo.gui;

import java.util.ArrayList;
import java.util.StringTokenizer;

import flexo.graphic.Camera;
import flexo.graphic.Color;
import flexo.input.Event;
import flexo.math.Coords;
import flexo.math.Vector;
import flexo.scene.Scene;
import flexo.common.LogMsgType;
import flexo.file.*;

public class Layout
{
	protected Camera cam;
	protected Scene scene;
	protected Theme theme;
	protected Cursor cursor;
	
	protected ArrayList<Widget> widgetList;
	protected ArrayList<ActiveWidget> activeWidgetsList;
	protected int activeWidget = -1;	

	public int getLayer()
	{
		return this.cam.getLayer();
	}
	
	public String getActiveWidget()
	{
		if (activeWidget >= 0)
		{
			return activeWidgetsList.get(activeWidget).getName();
		}
		return null;
	}
	
	public Layout(int layer, String layoutFile, Theme theme)
	{
		flexo.graphic.GraphicSystem gs = (flexo.graphic.GraphicSystem)flexo.Kernel.getSystem(flexo.graphic.GraphicSystem.ID);
		flexo.scene.SceneSystem ss = (flexo.scene.SceneSystem)flexo.Kernel.getSystem(flexo.scene.SceneSystem.ID);
		flexo.file.FileSystem fs = (flexo.file.FileSystem)flexo.Kernel.getSystem(flexo.file.FileSystem.ID);
		
		// Check the layoutFile exists		
		File f = fs.createFile(layoutFile);
		// Create camera & scene
		scene = ss.addScene(layoutFile);
		cam = gs.addCameraOrtho(layoutFile);
		cam.setLayer(layer);
		// Transparent background color
		cam.setBackgroundColor(new flexo.graphic.Color(0,0,0,0));
		
		this.theme = theme;
		
		// Load the widgets from file
		loadFromFile(f);

		// Load the cursor
		float cursorSize = 0.05f;
		Coords cSize = undoAspectRatio(relativeToAbsoluteCoords(new Coords(cursorSize,cursorSize)),true);
		cursor = theme.createCursor(cSize, new Coords());
		cursor.attachTo(scene.addChild("CursorNode"));
	}
	
	public void setCursorEnabled(boolean value)
	{
		if (value)
		{
			cursor.attachTo(scene.getChild("CursorNode"));
		}
		else
		{
			cursor.deattach();
		}
	}
	
	public void setCursorSensitivity(float value)
	{
		cursor.setSensitivity(value);
	}
	
	/**
	 * Apply a reversed aspect-ratio to mantain the same size
	 * on all resolutions.
	 * @param c Original size
	 * @return Non resulution dependant size
	 */
	protected Coords undoAspectRatio(Coords c, boolean keepHeight)
	{
		flexo.graphic.GraphicSystem gs = (flexo.graphic.GraphicSystem)flexo.Kernel.getSystem(flexo.graphic.GraphicSystem.ID);
		Vector screen = gs.getDisplayMode();
		if (keepHeight)
		{
			return new Coords(c.x * (screen.y / screen.x), c.y);
		}
		else
		{
			return new Coords(c.x, c.y * (screen.x / screen.y));
		}
	}
	
	protected Coords relativeToAbsoluteCoords(Coords c)
	{
		flexo.graphic.GraphicSystem gs = (flexo.graphic.GraphicSystem)flexo.Kernel.getSystem(flexo.graphic.GraphicSystem.ID);
		Vector screen = gs.getDisplayMode();
		return relativeToAbsoluteCoords(c, new Coords(screen.x,screen.y));
	}
	
	protected Coords relativeToAbsoluteCoords(Coords c, Coords absolute)
	{
		return new Coords(c.x * absolute.x, c.y * absolute.y);
	}
	
	protected void loadWidgetFromFile(flexo.file.Node fileNode, Widget parent)
	{			
		Widget w = null;
		
		//---------------------------------------------------
		// Name
		//---------------------------------------------------
		String name = "";
		if (fileNode.existsProperty("name"))
		{
			name = fileNode.getProperty("name").getStringValue();
		}
		
		//---------------------------------------------------
		// Size
		//---------------------------------------------------
		Coords size = new Coords(1,1);
		if (fileNode.existsProperty("width"))
		{
			size.x = fileNode.getProperty("width").getFloatValue();
			
			if (!fileNode.existsProperty("height") && fileNode.existsProperty("aspectRatio"))
			{
				float aspectRatio = fileNode.getProperty("aspectRatio").getFloatValue();
				size.y = size.x / aspectRatio;
				size = undoAspectRatio(size, false);
			}
		}
		if (fileNode.existsProperty("height"))
		{
			size.y = fileNode.getProperty("height").getFloatValue();
			
			if (!fileNode.existsProperty("width") && fileNode.existsProperty("aspectRatio"))
			{
				float aspectRatio = fileNode.getProperty("aspectRatio").getFloatValue();
				size.x = size.y * aspectRatio;
				size = undoAspectRatio(size, true);
			}
		}
		
		size.clamp(0, 1);			
		
		if (parent == null)
		{
			size = relativeToAbsoluteCoords(size);
		}
		else
		{
			size = relativeToAbsoluteCoords(size,parent.getSize());
		}			
		
		//---------------------------------------------------
		// Position
		//---------------------------------------------------
		Coords position = new Coords(0.5f,0.5f);
		if (fileNode.existsProperty("x"))
		{
			position.x = fileNode.getProperty("x").getFloatValue();
		}
		if (fileNode.existsProperty("y"))
		{
			position.y = fileNode.getProperty("y").getFloatValue();
		}
		position.clamp(0, 1);
		if (parent == null)
		{
			position = relativeToAbsoluteCoords(position);
		}
		else
		{
			Coords parentSize = parent.getSize();
			position = relativeToAbsoluteCoords(position,parentSize);
			position.x -= parentSize.x / 2.0f;
			position.y -= parentSize.y / 2.0f;
		}
		
		//---------------------------------------------------
		// Align
		//---------------------------------------------------
		Coords align = new Coords();
		if (fileNode.existsProperty("vAlign"))
		{
			align.y = fileNode.getProperty("vAlign").getFloatValue();
		}
		if (fileNode.existsProperty("hAlign"))
		{
			align.x = fileNode.getProperty("hAlign").getFloatValue();
		}
		align.clamp(-1, 1);		
		// Apply alignment
		position.x += (align.x * size.x * 0.5f);
		position.y += (align.y * size.y * 0.5f);
		
		//---------------------------------------------------
		// Widget type
		//---------------------------------------------------
		
		// PANEL --------------------------------------------
		if (fileNode.name.equals("panel"))
		{
			w = theme.createPanel(name,size);			
		}
		// IMAGE ---------------------------------------------
		else if (fileNode.name.equals("image"))
		{
			if (fileNode.existsProperty("text"))
			{
				String src = fileNode.getProperty("text").getStringValue();
				w = theme.createImage(name,size,src);
			}
		}
		// TEXTAREA AND LABEL-----------------------------------------
		else if (fileNode.name.equals("textarea") || fileNode.name.equals("label"))
		{		
			// Common attributes
			
			// Font Color
			Color fontColor = null;
			if (fileNode.existsProperty("color"))
			{
				StringTokenizer st = new StringTokenizer(fileNode.getProperty("color").getStringValue(),",");
				if (st.countTokens() == 3)
				{
					Vector col = new Vector(Integer.parseInt(st.nextToken()),
							Integer.parseInt(st.nextToken()),
							Integer.parseInt(st.nextToken()));
					
					col.clamp(0, 255);
					
					fontColor = new Color((int)col.x,(int)col.y,(int)col.z);
				}
			}
			
			// Font description
			String fontDesc = theme.getDefaultFont();
			if (fileNode.existsProperty("font"))
			{
				fontDesc = fileNode.getProperty("font").getStringValue();
			}
			
			// TextArea only attributes
			if (fileNode.name.equals("textarea"))
			{
				// Font Size
				Coords fontSize = new Coords(0,0.4f);
				if (fileNode.existsProperty("size"))
				{
					fontSize.y = fileNode.getProperty("size").getFloatValue();				
				}
				fontSize.clamp(0, 1);
				if (parent == null)
				{
					fontSize = relativeToAbsoluteCoords(fontSize);
				}
				else
				{
					fontSize = relativeToAbsoluteCoords(fontSize,parent.getSize());
				}
				
				// Create Widget
				TextArea t = theme.createTextArea(name, size, fontDesc, fontSize.y);
				
				// Font color
				if (fontColor != null)
				{
					t.setColor(fontColor);
				}
				
				// Font text
				if (fileNode.existsProperty("text"))
				{
					t.setText(fileNode.getProperty("text").getStringValue());
				}
				w = t;
			}
			// Label only attributes
			else
			{
				// Create Widget
				Label t = theme.createLabel(name, size,fontDesc);
				if (fileNode.existsProperty("text"))
				{
					t.setText(fileNode.getProperty("text").getStringValue());
				}
				w = t;
			}
		}		
		// BUTTON -------------------------------------------
		else if (fileNode.name.equals("button"))
		{
			String text = "";
			if (fileNode.existsProperty("text"))
			{
				text = fileNode.getProperty("text").getStringValue();
			}
			Button b = theme.createButton(name, size, text);
			activeWidgetsList.add(b);
			w = b;
		}
		else
		{
			flexo.Kernel.log("flexo.gui.Layout", "'"+fileNode.name+"' widget is unknown", LogMsgType.WARNING);
			flexo.Kernel.error();
		}
		
		//---------------------------------------------------
		// SceneNode creation
		//---------------------------------------------------
		flexo.scene.Node sceneNode;
		float currentZ;
		if (parent == null)
		{
			// No parent node
			currentZ = -1;
			sceneNode = scene.addChild(name);
		}
		else
		{
			currentZ = 0.1f;
			sceneNode = parent.getNode().addChild(name);
		}		
		sceneNode.setPosition(new Vector(position.x,position.y,currentZ));
		w.attachTo(sceneNode);			
		
		widgetList.add(w);
		
		//---------------------------------------------------
		// Load inner widgets
		//---------------------------------------------------
		
		for (int i = 0; i < fileNode.getChildrenCount(); ++i)
		{
			loadWidgetFromFile(fileNode.getChild(i), w);
		}
	}

	protected void loadFromFile(File f)
	{
		f.load(new flexo.file.reader.Xml());
		Node root = f.getRootNode();
		
		Node layout = root.getChild("layout");			
		
		widgetList = new ArrayList<Widget>(layout.getChildrenCount());
		activeWidgetsList = new ArrayList<ActiveWidget>();
		
		for (int i = 0; i < layout.getChildrenCount(); ++i)
		{
			loadWidgetFromFile(layout.getChild(i), null);
		}
		activeWidgetsList.trimToSize();		
	}
	
	public void remove()
	{
		cam.remove();
		scene.remove();
		
		flexo.input.InputSystem input = (flexo.input.InputSystem)flexo.Kernel.getSystem(flexo.input.InputSystem.ID);
	}
	
	public void update()
	{
		Coords mousePos = cursor.getCoords();
		// Update the active widget
		if (activeWidget < 0 || !activeWidgetsList.get(activeWidget).isMouseOver(mousePos.x, mousePos.y));
		{
			// Return the active widget to the standard state
			if (activeWidget >= 0)
			{
				activeWidgetsList.get(activeWidget).standard();
			}
			
			activeWidget = -1;
			for (int i = 0; i < activeWidgetsList.size() && activeWidget < 0; ++i)
			{
				ActiveWidget aw = activeWidgetsList.get(i);
				if (aw.isMouseOver(mousePos.x, mousePos.y))
				{
					activeWidget = i;
				}
			}
		}
		
		// If there's an active widget
		if (activeWidget >= 0)
		{
			// Do a 'over'
			activeWidgetsList.get(activeWidget).over();
		}
	}
	
	public boolean event()
	{
		if (activeWidget >= 0)
		{
			return true;
		}
		return false;
	}
}
